﻿//------------------------------------------------------------------------------
// Copyright © FRA & FV 2014
// All rights reserved
//------------------------------------------------------------------------------
// Binary Serialization SDK
//
// SVN revision information:
//   $Revision: 96 $
//------------------------------------------------------------------------------
using System;
using System.IO;

namespace FRAFV.Binary.Serialization
{
	/// <summary>
	/// Binary stream extension methods
	/// </summary>
	public static class BinStreamExtensions
	{
		static byte[] buffer = new byte[8192];

		/// <summary>
		/// Rest length of the stream.
		/// </summary>
		/// <param name="reader">Binary reader.</param>
		/// <returns>The length to the end of the stream.</returns>
		public static long Length(this BinaryReader reader)
		{
			return reader.BaseStream.Length - reader.BaseStream.Position;
		}

		/// <summary>
		/// Advances the current position by that number of bytes.
		/// </summary>
		/// <param name="reader">Binary reader.</param>
		/// <param name="size">The number of bytes to skip.</param>
		/// <returns>
		/// The number of skipped bytes. This might be less than the number of bytes requested if that many bytes
		/// are not available, or it might be zero if the end of the stream is reached.
		/// </returns>
		public static long Skip(this BinaryReader reader, long size = -1L)
		{
			return Skip(reader.BaseStream, size);
		}

		/// <summary>
		/// Advances the stream current position by that number of bytes.
		/// </summary>
		/// <param name="stream">The stream.</param>
		/// <param name="size">The number of bytes to skip.</param>
		/// <returns>
		/// The number of skipped bytes. This might be less than the number of bytes requested if that many bytes
		/// are not available, or it might be zero if the end of the stream is reached.
		/// </returns>
		public static long Skip(this Stream stream, long size = -1L)
		{
			if (stream.CanSeek)
			{
				long pos = stream.Position;
				if (size < 0L) size = stream.Length - stream.Position;
				return stream.Seek(size, SeekOrigin.Current) - pos;
			}

			long length = 0L;
			if (size < 0L) size = long.MaxValue;
			while (size > 0L)
			{
				int bytesRead = stream.Read(buffer, 0, (int)Math.Min(size, buffer.Length));
				if (bytesRead == 0)
					break;
				length += bytesRead;
				size -= bytesRead;
			}
			return length;
		}

		/// <summary>
		/// Writes a specified number of copies of a byte to the current stream.
		/// </summary>
		/// <param name="writer">Binary Writer.</param>
		/// <param name="data">The byte to write.</param>
		/// <param name="repeatCount">The number of times to append value.</param>
		public static void Write(this BinaryWriter writer, byte data, int repeatCount)
		{
			Write(writer.BaseStream, data, repeatCount);
		}

		/// <summary>
		/// Writes a specified number of copies of a byte to the stream.
		/// </summary>
		/// <param name="stream">The stream.</param>
		/// <param name="data">The byte to write.</param>
		/// <param name="repeatCount">The number of times to append value.</param>
		public static void Write(this Stream stream, byte data, int repeatCount)
		{
			if (repeatCount < buffer.Length)
			{
				Array.Clear(buffer, 0, repeatCount);
				stream.Write(buffer, 0, repeatCount);
				return;
			}
			Array.Clear(buffer, 0, buffer.Length);
			while (repeatCount > 0)
			{
				int bytesWrite = Math.Min(repeatCount, buffer.Length);
				stream.Write(buffer, 0, bytesWrite);
				repeatCount -= bytesWrite;
			}
		}

		/// <summary>
		/// Writes the entire contents of the stream to another stream.
		/// </summary>
		/// <param name="src">Source stream.</param>
		/// <param name="dst">The stream to write this stream to.</param>
		/// <param name="size">The number of bytes to skip.</param>
		public static void WriteTo(this Stream src, Stream dst, int size = -1)
		{
			if (size < 0) size = int.MaxValue;
			while (size > 0)
			{
				int bytesRead = src.Read(buffer, 0, Math.Min(size, buffer.Length));
				if (bytesRead == 0)
					break;
				dst.Write(buffer, 0, bytesRead);
				size -= bytesRead;
			}
		}
	}
}
